<?php
/**
 * visitor.class
 *
 * Класс для хранения данных о посетителе
 * Visitor class for keeping data of current visitor
 *
 * @version:	0.4
 * @package:	flame.core
 * @author:		alex_ez
 * @created:	11.11.2008 16:17:50
 * @link:		http://qfox.ru/
 *
 * @copyright:	(c) 2008 flamebox.ru. All rights reserved.
 */

class visitor extends singleton {
	// php singleton wrapper
	static public function &instance() {
		return parent::instance(__CLASS__);
	}

	private $_started = null;

	private $name = null, $mail = null, $time = null, $life = null;
	private $auth_controller_name = null;
	
	protected $session;

	protected $flash_messages_keep = array();
	protected $flash_messages = null;

	/**
	 * Конструктор
	 *
	 * @access protected
	 */
	protected function __construct( ) {
		if( defined( 'VISITOR_INSTANCE' ) ) {
			exec( 'echo '.escapeshellarg('second visitor instance').' >> '.escapeshellarg(root.'.sesslog') );
			return null;
		}
		define( 'VISITOR_INSTANCE', 1 );

		// Запускаем сессию, если она еще не запущена
		$r = request::instance();
		$this->session = session::instance();

		$this->session->start();

		// for flash-queries sid allowed in GET only. flush cookie
		if( $r ->is_flash() ) {
			//ini_set( 'session.use_cookies', 0 );
			//ini_set( 'session.use_only_cookies', 0 );
		}

		/**
		 * Всяка шняга
		 */
		if( isset( $r ->c_visitor ) )
			try {
				$_data = @unserialize( base64_decode( str_rot13( $r->c_visitor ) ) );
				if (bc::enabled()) bc::log( $_data, 'cookies' );
				$this ->time = isset( $_data['time'] ) ? $_data['time'] : time( );
				$this ->name = isset( $_data['name'] ) ? $_data['name'] : null;
				$this ->mail = isset( $_data['mail'] ) ? $_data['mail'] : null;
				$this ->life = isset( $_data['life'] ) ? (int)($_data['life']) : null;
			} catch( Exception $e ) {
				$this ->time = time( );
				$this ->name = 'johndoe';
				$this ->mail = null;
			}
		$session_Life_Time = ini_get( 'session.cookie_lifetime' );
		if( !is_null( $this ->life ) )
			ini_set( 'session.cookie_lifetime', $this ->life );
		//setcookie( 'dlife', serialize($session_Life_Time), 0 , '/', ini_get('session.cookie_domain') );
		//setcookie( 'life', serialize($this ->life), 0 , '/', ini_get('session.cookie_domain') );

		//if( $r ->is_flash() && !$r->g_sid ) response::instance()->teapot();
		//$sid = $r ->is_flash() ? $r ->g_sid : $r ->c_sid;

		// set session_id
		//if( isset($sid)&&!is_null($sid)&&strlen($sid)>10 ) session_id( $sid );
		// restart old session (or generate new one)
		//@session_start();
		//$sid = session_id();
		$sid = $this->session->sid();
		//exec( 'echo '.escapeshellarg($r->get_Ip().' '.$sid).' >> '.escapeshellarg(root.'.sesslog') );

		if (bc::enabled()) bc::group('visitor', array('Collapsed' => true, 'Color' => '#777'));
		if (bc::enabled()) bc::log($sid, 'sid');
		if( $this ->signed ) {
			if (bc::enabled()) bc::log( $this ->signed, 'id' );
			if (bc::enabled()) bc::log( $this ->user['login'], 'login' );
			if (bc::enabled()) bc::log( $this ->_roles, 'roles' );
		}
		else {
			$this ->life = null;
			ini_set( 'session.cookie_lifetime', $session_Life_Time );
		}
		
		$s = $this->session->data();
		unset( $s['user'] );
		unset( $s['_roles'] );
		unset( $s['signed'] );
		if (bc::enabled()) bc::log( $s, 'session' );
		unset( $s );

		// Устанавливаем локаль — убрано в конфиг
		//setlocale( LC_ALL, 'ru_RU.UTF-8' );
		//setlocale( LC_NUMERIC, 'C' );
		mb_internal_encoding( 'utf8' );

		if (bc::enabled()) bc::group_End( );

		$flash = $this ->flash;
		if( isset( $this ->flash ) && !empty( $flash ) && is_array( $flash ) )
			$this ->flash_messages = $flash;

		$this ->_started = true;
	}

	public function __destruct( ) {
		if( !$this ->_started ) return;

		$data = array( );
		$data['time'] = $this ->time ? $this ->time : time( );
		if( $this ->name ) $data['name'] = $this ->name;
		if( $this ->mail ) $data['mail'] = $this ->mail;
		if( !is_null( $this ->life ) ) $data['life'] = $this ->life;

		setcookie( 'visitor', str_rot13( base64_encode( serialize( $data ) ) ), strtotime( "next year" ), '/', ini_get('session.cookie_domain') );

		unset( $this ->flash );

		if(!empty( $this ->flash_messages ) && is_array($this ->flash_messages) && !empty($this->flash_messages_keep)) {
			$keep = array();
			foreach($this ->flash_messages_keep as $key){
				if(isset($this ->flash_messages[$key])){
					$keep[$key] = $this ->flash_messages[$key];
				}
			}
			$this->flash = $keep;
		}

		$this->_started = false;
	}


	public function signin( $user, $time = null ) {

		if (bc::enabled()) bc::info($user, 'signin');

		$this ->signed = $user['id'];
		$this ->user = $user;
		$this ->_roles = explode( ',', @$user['roles'] );

		/*if( !is_null( $time ) ) {
			ini_set( 'session.cookie_lifetime', $time );
			$this ->life = $time;
		}*/
	}

	public function signout( ) {
		$this ->signed = null;
		$this ->user = null;
		$this ->_roles = null;
		$this ->life = null;
		$this ->referer = null;
	}

	public function refresh( ) {
		if( !$this ->is_signed( ) ) return null;
		return $this ->get_auth_controller( ) ->resign( );
	}

	public function is_signed( ) {
		return !is_null( $this ->user_id() );
	}

	public function need_auth( $state = null, $strict = null ) {
		// shout ajax, flash queries and ugly queries from web
		$request = request::instance( );
		if( $request ->is_bot() || !$this ->is_signed() && ( $request ->is_ajax() || $request ->is_flash() ) )
			response::instance() ->forbidden( ); //403

		$config = config::instance( );

		// redirects
		$url = $request ->get_uri();
		$path = $this ->get_auth_path();
		$signin_url = $path . 'signin/';

		if( $url != $signin_url && $url != $path && $url != '/' ) {
			$this ->referer = $url;
		}

		if(!$this ->is_signed()) {
			response::instance() ->seeother($signin_url);
		}

		// cut! or not?..
		$strict = is_null($strict) ? ( $config ->roles == 'strict' ) : $strict;
		$state = is_null($state) ? 'person' : $state; // default state is person.

		if( $strict && $this ->user['state'] != $state ) {
			$must_be_an_url = str_replace( '/person/', '/', '/'.$this ->user['state'].'/' );
			if( $url != $must_be_an_url )
			//if (bc::enabled()) bc::log( array( 'role' => $state, 'state' => $this ->user['state'] ) );
				response::instance() ->seeother( $must_be_an_url );
		}
	}

	/**
	 * move to auth controller
	 */
	public function get_auth_path( ) {
		return site_url($this ->get_auth_controller_name());
	}

	public function get_auth_controller( ) {
		return controller::instance_of( $this ->get_auth_controller_name( ) );
	}

	public function get_auth_controller_name( ) {
		if( is_null( $this ->auth_controller_name ) )
			$this ->auth_controller_name = trim(_::pick(config::instance( ) ->auth__controller,'auth'),"\r\n\t /\\");

		return $this ->auth_controller_name;
	}

	public function __set( $k, $v = null ) {
		$this->session->set_data($k, $v);
	}

	public function &__get( $k ) {
		switch( $k ) {
			case 'name':
			case 'mail':
			case 'time': return $this ->$k;
				break;
			case 'roles':
				$roles = new roles( $this ->_roles );
				return $roles;
			break;
			case 'viewuser':
				$_user = $this ->user;
				unset( $_user[ 'password' ] );
				$viewvisitor = new viewuser( $_user );
				return $viewvisitor;
			break;
			default:
				return $this->session->data($k);
		}
	}

	public function __isset( $k ) {
		return $this->session->isset_data($k);
	}

	public function __unset( $k ) {
		$this->session->unset_data($k);
	}

	/**
	 * some methods
	 *
	 */
	public function user_id( ) {
		return $this ->signed;
	}

	public function user_login( ) {
		return $this ->user['login'];
	}

	public function user_info( ) {
		$us = new users( );
		$r = $us ->get( $this ->signed );
		$user = array( );
		foreach( explode( ',', 'id,name,state' ) as $k )
			$user[ $k ] = $r[ $k ];

		return $user;
	}

	public function sid() {
		return $this->session->sid();
	}

	// flash data
	public function flash( $key = null, $message = null, $url = null ) {
		switch( is_null( $key ) ? 0 : ( is_null( $message ) ? 1 : 2 ) ) {
			case 0:
				return (array)($this ->flash_messages);
			case 1:
				if( isset( $this ->flash_messages[$key] ) )
					return $this ->flash_messages[$key];
				return null;
				break;
			case 2:
				$this ->keep_flash( $key );
				$this ->flash_messages[$key] = $message;
				if(!empty($url))
					response::instance()->seeother($url);
		}
		return $this;
	}

	public function keep_flash( $key ) {
		$this->flash_messages_keep[] = $key;
		return $this;
	}

	public function keep_flash_all( ) {
		if (is_array($this->flash_messages))
			foreach (array_keys($this->flash_messages) as $key)
				$this->flash_messages_keep[] = $key;
		return $this;
	}
}

